# Copyright (C) 2005-2006 Frederic Back <fredericback@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.

import threading
import string

try: import psycopg
except: import psycopg2.psycopg1 as psycopg

import GeoTypes
from GeoTypes import Box, Point

import time

def joinBoxes(box_a, box_b):
    ''' Union two boxes. Useful to join geometry extents '''
    a_ll = box_a.getLowerLeft()
    b_ll = box_b.getLowerLeft()
    a_ur = box_a.getUpperRight()
    b_ur = box_b.getUpperRight()
    if a_ll.getX() < b_ll.getX(): mx = a_ll.getX()
    else: mx = b_ll.getX()
    if a_ll.getY() < b_ll.getY(): my = a_ll.getY()
    else: my = b_ll.getY()
    if a_ur.getX() > b_ur.getX(): Mx = a_ur.getX()
    else: Mx = b_ur.getX()
    if a_ur.getY() > b_ur.getY(): My = a_ur.getY()
    else: My = b_ur.getY()
    return Box( Point(mx,my), Point(Mx,My) )

class Table:
    """
        schema the table is in
        table name
        extent
        columns:    column name, column type, [geometry type], [srid]
        Example:    ['sym2002', 'int8', None, None]
                    ['gid', 'int2', None, None]
                    ['the_geom', 'geometry', 'OGMultiLineString', -1] 
    """

    linestrings = 'OGMultiLineString'

    def __init__(self, schema, name):
        self.schema = schema
        self.name = name
        self.extent = None
        self.columns = []
        self.hasGeometry = False

    def getSQL(self):
        """ Return a string suitable to identify this table in an SQL statement. """
        return '"%s"."%s"'%(self.schema,self.name)

    def get_geometry_columns(self):
        f = lambda c: c[1] == "geometry"
        return filter(f, self.columns)

    def get_numeric_columns(self):
        f = lambda c: c[1][:3] == "int" or c[1][:5] == "float" or c[1] == "numeric"
        return filter(f, self.columns)
        
    def getGeomType(self):
        ''' scan columns, return cummulated geometry type
            'line','polygon','point' or 'multi'    '''
        geom = None
        for c in self.columns:
            if c[2] == None: continue
            if c[2][-10:].upper() == "LINESTRING":
                if geom == None or geom == "line": geom = "line"
                else: geom = "multi"
            elif c[2][-7:].upper() == "POLYGON":
                if geom == None or geom == "polygon": geom = "polygon"
                else: geom = "multi"
            elif c[2][-5:].upper() == "POINT":
                if geom == None or geom == "point": geom = "point"
                else: geom = "multi"
            else: geom = "multi"

        return geom

def getGeometryExtent(db, table, geomcol = "the_geom"):
    """ return a GeoTypes.Box() """
    c = db.handle.cursor()
    c.execute('SELECT extent(%s) FROM %s' %(geomcol,table))
    r = c.fetchone()[0]
    b = Box()
    b.fromWKT(r)
    return b
    c.close()

class Database:
    def __init__(self):
        self.connected = False
        self.host = "localhost"
        self.user = ""
        self.pw = ""
        self.dbname = ""
        self.port = ""

    def __del__(self):
        self.close()

    def clone(self):
        d = Database()
        d.host = self.host
        d.user = self.user
        d.pw = self.pw
        d.dbname = self.dbname
        d.port = self.port
        return d

    def functionAvailable(self, routineName):
        """ Return true if a function is available in the db """   
        # do not catch errors here
        c=self.handle.cursor()
        c.execute("select * from information_schema.routines where routine_name='%s'"%routineName)
        return c.rowcount > 0
        
    def connect(self, constr=""):
        self.connected = False
        if(constr==""): # if connection string is not manually specified
            if(self.host!=""): constr = constr + 'host=%s ' %self.host
            if(self.dbname!=""): constr = constr + 'dbname=%s ' %self.dbname
            if(self.user!=""): constr = constr + 'user=%s ' %self.user
            if(self.pw!=""): constr = constr + 'password=%s ' %self.pw
            if(self.port!=""):
                try:
                    port = string.atoi(self.port)
                    constr = constr + 'port=%d ' %port
                except Exception, e:
                    print 'The specified port (%s) is invalid: it must be an integer' % self.port

        print constr

        # hook GeoTypes into psycopg
        try:
            GeoTypes.initialisePsycopgTypes(psycopg, {}, constr, True)
        except psycopg.ProgrammingError, e:
            # "select postgis_full_version()" probably failed (run by GeoTypes) 
            print "Could not initialise GeoTypes:"
            print e

        try:
            self.handle = psycopg.connect(constr)
            self.connected = True
            #print "connection established (%s)" %constr
        except psycopg.OperationalError, e:
            # operational errors are returned by the database, for instance when
            # a connection cannot be established.

            # don't catch them here

            print e
            raise e


    def close(self):
        try: self.handle.close()
        except: pass
